package com.nhncorp.pleiades.client;

import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.nhncorp.pleiades.conf.PleiadesConfiguration;
import com.nhncorp.pleiades.hadoop.New_RPC;
import com.nhncorp.pleiades.protocol.EventMetaData;
import com.nhncorp.pleiades.protocol.EventServiceProtocol;
import com.nhncorp.pleiades.util.NetworkUtil;
import com.nhncorp.pleiades.util.Sleeper;

public class EventServiceClient implements EventService, Runnable {
  
  public static final Log LOG = LogFactory.getLog(EventServiceClient.class
      .getName());  
  protected PleiadesConfiguration conf;
  private PleiadesUser userInfo = null;
  private Sleeper sleeper = null;
  private EventServiceProtocol eventService = null;
  protected final AtomicBoolean stopRequested = new AtomicBoolean(false);
  private String masterPleiades = "";
  private String[] slavePleiades = null;  
  
  public EventServiceClient(PleiadesConfiguration conf) throws IOException {
    try {
      this.conf = conf;
      this.userInfo = new PleiadesUser("default");
      init();
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(e.getMessage());
    }
  }
  
  public EventServiceClient(PleiadesUser userInfo, PleiadesConfiguration conf) throws IOException {
    try {
      this.conf = conf;    
      this.userInfo = userInfo;
      this.sleeper = new Sleeper(1000, this.stopRequested);
      init();
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(e.getMessage());
    }
  }  
  
  private void init() throws IOException {
    System.setProperty("java.net.preferIPv4Stack", "true");    
    InetAddress[] inetAddress;
    try {
      inetAddress = InetAddress.getAllByName(conf.get("pleiades.eventmanager.name", ClientConstants.CELL_NAME));
      String[] eventManagers = conf.getStrings("pleiades.eventmanager.name");
      if(eventManagers != null) {
        for(String tempManager : eventManagers) {
          inetAddress = InetAddress.getAllByName(tempManager);
          for(InetAddress entry : inetAddress) {
            String address = entry.getHostAddress();
            if(entry.isReachable(ClientConstants.MAX_LOOKUP_TIME)) {
              LOG.info("ip address for event manager : " + address);
              lookupPleiadesCell(address);
              break;
            }
          }
        }
      }
      this.sleeper = new Sleeper(1000, this.stopRequested);
    } catch (Exception e) {
      throw new IOException(e.getMessage());
    } 
  }
  
  private void lookupPleiadesCell(String address) throws IOException{
    
    masterPleiades = address + ":" + String.valueOf(ClientConstants.DEFAULT_EVENT_PORT);
    LOG.info("lookup pleiades cell : " + this.masterPleiades);
    eventService = connectPleiadesCell();
    if(!isEventServiceReady()) {
      throw new IOException("event service is not ready " + this.masterPleiades);
    }
  }  
  
  private EventServiceProtocol connectPleiadesCell() throws IOException {
    return connectPleiadesCell(this.masterPleiades);
  }
  
  private EventServiceProtocol connectPleiadesCell(String server) throws IOException {
    int maxRetry = 10;
    int retry = 0;
    while (retry < maxRetry) {
      try {
        LOG.debug("try to connect pleiades cell : " + server);
        if( eventService == null) {
          eventService = (EventServiceProtocol) New_RPC.getProxy(
              EventServiceProtocol.class, EventServiceProtocol.versionID,
              NetworkUtil.getAddress(server), conf);         
        }
        break;
      } catch (Exception e) {
        LOG.debug("try " + retry + " got exception while trying to connect pleiades cell");
        eventService = null;
        retry++;
        if (retry >= maxRetry) {
          e.printStackTrace(System.out);
          break;
        }
        sleeper.sleep();
      }
    }
    return eventService;
  }
  
  private boolean isEventServiceReady() throws IOException {
    boolean isSuccess = false;
    String prevMasterPleiades = "";
    int maxWaitTime = 0;
    try {
      while (maxWaitTime < 10) {
        if (eventService.isEventManagerReady()) {
          isSuccess = true;
          break;
        }
        ++maxWaitTime;
        LOG.fatal("-----event service is not ready : " + this.masterPleiades + " -----");
        prevMasterPleiades = this.masterPleiades;
//        this.masterPleiades = eventService.getMasterPleiades();
        if (this.masterPleiades == null || this.masterPleiades.equals("")) {
          LOG.fatal("masterPleiades is null and trying to update masterPleiades");
//          this.masterPleiades = updatePleiadesMaster();
          eventService = null;
//          isUpdatedMasterPleiades.set(true);
        } 
        if(!this.masterPleiades.equals(prevMasterPleiades)) {
          eventService = null;
        }
        eventService = connectPleiadesCell(this.masterPleiades);
      }
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(e.getMessage());
    } finally {
      if (!isSuccess) {
        eventService = null;
//        isUpdatedMasterPleiades.set(false);
      }
    }
    return isSuccess;
  }  

  
  public SpaceEntry[] readEntry(SpaceEntry entry, int maxCount, int timeOut) {
    eventService.read(new EventMetaData(entry.getType(), userInfo.getHostName()));
    return null;
  }

  
  public SpaceEntry[] takeEntry(SpaceEntry entry, int maxCount, int timeOut) {
    // TODO Auto-generated method stub
    return null;
  }
  
  
  public boolean subscribe(Subscription subscription) {
    boolean isSuccess = false;
    try {
      eventService = connectPleiadesCell();
      
      
      
    } catch (Exception e) {
      LOG.fatal(e.getMessage());
    } finally {
      if(!isSuccess)
        return isSuccess;
    }
    return false;
  }
  
  
  public boolean unSubscribe(Subscription subscription) {
    // TODO Auto-generated method stub
    return false;
  }

  
  public boolean writeEntry(SpaceEntry entry) {
    boolean isSuccess = false;
    try {
      eventService = connectPleiadesCell();
      EventMetaData eventMetaData = new EventMetaData(entry.getType(), userInfo
          .getHostName());
      eventMetaData.setEventKey(entry.getKey());
      eventMetaData.setEventValue(entry.getValue());
      eventService.write(eventMetaData);
      isSuccess = true;
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if(!isSuccess)
        return isSuccess;
    }
    return false;
  }

  
  public boolean writeEntry(List<SpaceEntry> entryList) {
    // TODO Auto-generated method stub
    return false;
  }

  
  public void run() {
    // TODO Auto-generated method stub
    
  }
  
  /**
   * @param args
   */
  public static void main(String[] args) {
    try {
      EventServiceClient client = new EventServiceClient(new PleiadesUser(""),
          new PleiadesConfiguration());
      Thread thread = new Thread(client);
      thread.start();
    } catch (IOException e) {
      e.printStackTrace();
    }

  }  

}
